<template>
    <div class="cropper">
        <div class="cropper-body">
            <div class="cropper-body-left">
                <div class="img-wrp">
                    <img :src="imgURL" alt="" ref="originImage">
                    <div class="showPosition" ref="showPosition" :style="`top: ${originHeightOffset}px;
                                left: ${originWidthOffset}px;
                                width: ${originShowWidth}px;
                                height: ${originShowHeight}px;`">
                        <!-- 裁剪框 -->
                        <div class="crop-box" ref="cropBox" :style="`width: ${boxWidth}px;
                                    height: ${boxHeight}px;
                                    top: ${boxTop}px;
                                    left: ${boxLeft}px;`">
                            <!-- 中间移动块 -->
                            <div class="crop-box-move" ref="cropBoxMove"></div>
                            <!-- 内侧左蒙版 -->
                            <div class="crop-box-marking-left"></div>
                            <!-- 内侧右蒙版 -->
                            <div class="crop-box-marking-right"></div>
                            <!-- 内侧上蒙版 不可见 -->
                            <div class="crop-box-marking-top"></div>
                            <!-- 内侧下蒙版 不可见 -->
                            <div class="crop-box-marking-bottom"></div>
                            <!-- 左上控块 -->
                            <div class="cropper-point point-lt" @mousedown="(e) => startResize('lt', e)"
                                @touchstart="(e) => startResize('lt', e.touches[0])"></div>
                            <!-- 右上控块 -->
                            <div class="cropper-point point-rt" @mousedown="(e) => startResize('rt', e)"
                                @touchstart="(e) => startResize('rt', e.touches[0])"></div>
                            <!-- 左下控块 -->
                            <div class="cropper-point point-lb" @mousedown="(e) => startResize('lb', e)"
                                @touchstart="(e) => startResize('lb', e.touches[0])"></div>
                            <!-- 右下控块 -->
                            <div class="cropper-point point-rb" @mousedown="(e) => startResize('rb', e)"
                                @touchstart="(e) => startResize('rb', e.touches[0])"></div>
                        </div>
                        <!-- 上蒙版 -->
                        <div class="marking marking-top" :style="`width: ${boxWidth}px;
                                    height: ${boxTop}px;
                                    top: 0px;
                                    left: ${boxLeft}px;`"></div>
                        <!-- 右蒙版 -->
                        <div class="marking marking-right" :style="`width: ${originShowWidth - (boxLeft + boxWidth)}px;
                                    height: ${originShowHeight}px;
                                    top: 0px;
                                    left: ${boxLeft + boxWidth}px;`"></div>
                        <!-- 下蒙版 -->
                        <div class="marking marking-bottom" :style="`width: ${boxWidth}px;
                                    height: ${originShowHeight - (boxTop + boxHeight)}px;
                                    top: ${boxTop + boxHeight}px;
                                    left: ${boxLeft}px;`"></div>
                        <!-- 左蒙版 -->
                        <div class="marking marking-left" :style="`width: ${boxLeft}px;
                                    height: ${originShowHeight}px;
                                    top: 0px;
                                    left: 0px;`"></div>
                    </div>
                </div>
                <div class="tips-wrp">
                    <div>当前分辨率 {{ cutWidth }}*{{ cutHeight }}</div>
                    <div>*部分情况下您的封面将以4:3比例展示</div>
                </div>
            </div>
            <div class="cropper-body-right">
                <div class="preview-item">
                    <div class="preview-title">16:9效果预览</div>
                    <div class="preview-card">
                        <div class="pic169">
                            <img :src="imgURL" alt=""
                                :style="`width: ${prev169Width}px;
                                        height: ${prev169Height}px;
                                        transform: translateX(-${prev169OffsetX}px) translateY(-${prev169OffsetY}px);`">
                        </div>
                        <div class="title"><span>{{ title }}</span></div>
                    </div>
                </div>
                <div class="preview-item">
                    <div class="preview-title">4:3效果预览</div>
                    <div class="preview-card">
                        <div class="pic43">
                            <img :src="imgURL" alt="" :style="`width: ${prev43Width}px;
                                        height: ${prev43Height}px;
                                        transform: translateX(-${prev43OffsetX}px) translateY(-${prev43OffsetY}px);`">
                        </div>
                        <div class="title"><span>{{ title }}</span></div>
                    </div>
                </div>
            </div>
        </div>
        <canvas ref="cropperCanvas" style="display: none"></canvas>
    </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue';

// 定义props
const props = defineProps({
    imgURL: {
        type: String,
        default: null
    },
    cutOrder: {
        type: Boolean,
        default: false
    },
    title: {
        type: String,
        default: "title"
    }
});

// 定义响应式数据
const width = ref(0);
const height = ref(0);
const boxWidth = ref(0);
const boxHeight = ref(0);
const boxTop = ref(0);
const boxLeft = ref(0);
const isResizing = ref(false);
const resizeDirection = ref('');
const startX = ref(0);
const startBoxWidth = ref(0);
const startBoxHeight = ref(0);
const startBoxLeft = ref(0);
const startBoxTop = ref(0);

// 计算属性
const originAspectRatio = computed(() => {
    return width.value / height.value;
});

const originShowWidth = computed(() => {
    if (originAspectRatio.value < 16 / 9) {
        return 270 * originAspectRatio.value;
    } else {
        return 480;
    }
});

const originShowHeight = computed(() => {
    if (originAspectRatio.value > 16 / 9) {
        return 480 / originAspectRatio.value;
    } else {
        return 270;
    }
});

const originWidthOffset = computed(() => {
    if (originAspectRatio.value < 16 / 9) {
        const showWidth = 270 * originAspectRatio.value;
        return (480 - showWidth) / 2;
    } else {
        return 0;
    }
});

const originHeightOffset = computed(() => {
    if (originAspectRatio.value > 16 / 9) {
        const showHeight = 480 / originAspectRatio.value;
        return (270 - showHeight) / 2;
    } else {
        return 0;
    }
});

// 计算裁剪框和裁剪结果
// 左偏移像素
const cutOffsetX = computed(() => Math.floor((boxLeft.value / originShowWidth.value) * width.value));
// 上偏移像素
const cutOffsetY = computed(() => Math.floor((boxTop.value / originShowHeight.value) * height.value));
// 裁剪图宽
const cutWidth = computed(() => Math.floor((boxWidth.value / originShowWidth.value) * width.value));
// 裁剪图高
const cutHeight = computed(() => Math.floor((boxHeight.value / originShowHeight.value) * height.value));

// 计算预览图的宽高 16:9
// 实际宽度
const prev169Width = computed(() => 224 * (originShowWidth.value / boxWidth.value));
// 实际高度
const prev169Height = computed(() => 126 * (originShowHeight.value / boxHeight.value));
// 实际偏移X
const prev169OffsetX = computed(() => prev169Width.value * (boxLeft.value / originShowWidth.value));
// 实际偏移Y
const prev169OffsetY = computed(() => prev169Height.value * (boxTop.value / originShowHeight.value));

// 计算预览图的宽高 4:3
// 实际宽度
const prev43Width = computed(() => 224 * (originShowWidth.value / (boxWidth.value * 0.75)));
// 实际高度
const prev43Height = computed(() => 168 * (originShowHeight.value / boxHeight.value));
// 实际偏移X
const prev43OffsetX = computed(() => prev43Width.value * ((boxWidth.value * 0.125 + boxLeft.value) / originShowWidth.value));
// 实际偏移Y
const prev43OffsetY = computed(() => prev43Height.value * (boxTop.value / originShowHeight.value));

// methods
const initBox = () => {
    if (originAspectRatio.value > 16 / 9) {
        boxHeight.value = 0.9 * originShowHeight.value;
        boxWidth.value = boxHeight.value * 16 / 9;
        boxTop.value = (originShowHeight.value - boxHeight.value) / 2;
        boxLeft.value = (480 - boxWidth.value) / 2;
    } else {
        boxWidth.value = 0.9 * originShowWidth.value;
        boxHeight.value = boxWidth.value / (16 / 9);
        boxTop.value = (270 - boxHeight.value) / 2;
        boxLeft.value = (originShowWidth.value - boxWidth.value) / 2;
    }
};

const updateOrigin = () => {
    const img = new Image();
    img.src = props.imgURL;
    img.onload = () => {
        width.value = img.width;
        height.value = img.height;
        setTimeout(() => {
            initBox();
        }, 1);
    };
};

const cropBoxMove = ref(null);
const cropBox = ref(null);
const showPosition = ref(null);

const initDrag = () => {


    let isDragging = false;
    let offsetX, offsetY;

    cropBoxMove.value.addEventListener("mousedown", (e) => {
        isDragging = true;
        offsetX = e.clientX - cropBox.value.getBoundingClientRect().left;
        offsetY = e.clientY - cropBox.value.getBoundingClientRect().top;
    });

    cropBoxMove.value.addEventListener("touchstart", (e) => {
        isDragging = true;
        offsetX = e.touches[0].clientX - cropBox.value.getBoundingClientRect().left;
        offsetY = e.touches[0].clientY - cropBox.value.getBoundingClientRect().top;
    });

    document.addEventListener("mousemove", (e) => {
        if (!isDragging) return;

        let left = e.clientX - offsetX - showPosition.value.getBoundingClientRect().left;
        let top = e.clientY - offsetY - showPosition.value.getBoundingClientRect().top;

        left = Math.max(0, left);
        left = Math.min(showPosition.value.offsetWidth - cropBox.value.offsetWidth, left);
        top = Math.max(0, top);
        top = Math.min(showPosition.value.offsetHeight - cropBox.value.offsetHeight, top);

        boxLeft.value = left;
        boxTop.value = top;
    });

    document.addEventListener("touchmove", (e) => {
        if (!isDragging) return;

        let left = e.touches[0].clientX - offsetX - showPosition.value.getBoundingClientRect().left;
        let top = e.touches[0].clientY - offsetY - showPosition.value.getBoundingClientRect().top;

        left = Math.max(0, left);
        left = Math.min(showPosition.value.offsetWidth - cropBox.value.offsetWidth, left);
        top = Math.max(0, top);
        top = Math.min(showPosition.value.offsetHeight - cropBox.value.offsetHeight, top);

        boxLeft.value = left;
        boxTop.value = top;
    });

    document.addEventListener("mouseup", () => {
        isDragging = false;
    });

    document.addEventListener("touchend", () => {
        isDragging = false;
    });
};

const startResize = (reDirection, event) => {
    isResizing.value = true;
    resizeDirection.value = reDirection;
    startX.value = event.clientX;
    startBoxWidth.value = boxWidth.value;
    startBoxHeight.value = boxHeight.value;
    startBoxLeft.value = boxLeft.value;
    startBoxTop.value = boxTop.value;

    window.addEventListener('mousemove', resize);
    window.addEventListener('touchmove', resize);
    window.addEventListener('mouseup', stopResize);
    window.addEventListener('touchend', stopResize);
};

// TODO: 缩放功能
// 缩放功能
const resize = (event) => {
    if (!isResizing.value) return;
    // 根据event值来判断是移动端还是电脑端
    const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
    const deltaX = clientX - startX.value;
    // 根据不同的缩放方向，计算新的尺寸和位置
    if (resizeDirection.value == 'lt') {
        // 左上缩放
        // 计算新的宽度和左偏移量
        const newWidth = startBoxWidth.value - deltaX;
        const newLeft = startBoxLeft.value + deltaX;
        // 比例缩放
        const newHeight = newWidth / (16 / 9);
        const newTop = startBoxTop.value + (startBoxHeight.value - newHeight);
        // 边界检查
        if (newWidth >= 64 && newWidth <= originShowWidth.value && newLeft >= 0 && newTop >= 0) {
            boxWidth.value = newWidth;
            boxHeight.value = newHeight;
            boxLeft.value = newLeft;
            boxTop.value = newTop;
        }
    } else if (resizeDirection.value == 'rt') {
        // 右上缩放
        const newWidth = startBoxWidth.value + deltaX;
        // 比例缩放
        const newHeight = newWidth / (16 / 9);
        const newTop = startBoxTop.value + (startBoxHeight.value - newHeight);
        // 边界检查
        if (newWidth >= 64 && newWidth + startBoxLeft.value <= originShowWidth.value && newTop >= 0) {
            boxWidth.value = newWidth;
            boxHeight.value = newHeight;
            boxTop.value = newTop;
        }
    } else if (resizeDirection.value == 'lb') {
        // 左下缩放
        const newWidth = startBoxWidth.value - deltaX;
        const newLeft = startBoxLeft.value + deltaX;
        // 比例缩放
        const newHeight = newWidth / (16 / 9);
        // 边界检查
        if (newWidth >= 64 && newWidth <= originShowWidth.value && newLeft >= 0 && startBoxTop.value + newHeight <= originShowHeight.value) {
            boxWidth.value = newWidth;
            boxHeight.value = newHeight;
            boxLeft.value = newLeft;
        }
    } else if (resizeDirection.value == 'rb') {
        // 右下缩放
        const newWidth = startBoxWidth.value + deltaX;
        // 比例缩放
        const newHeight = newWidth / (16 / 9);
        // 边界检查
        if (newWidth >= 64 && newWidth + startBoxLeft.value <= originShowWidth.value && startBoxTop.value + newHeight <= originShowHeight.value) {
            boxWidth.value = newWidth;
            boxHeight.value = newHeight;
        }
    }  
    // 阻止默认事件          
    if (!(event.touches && event.touches.length > 0)) {
        event.preventDefault();
    }
};

const stopResize = () => {
    window.removeEventListener('mousemove', resize);
    window.removeEventListener('touchmove', resize);
    window.removeEventListener('mouseup', stopResize);
    window.removeEventListener('touchend', stopResize);
    isResizing.value = false;
    resizeDirection.value = '';
};

const emit = defineEmits(["update:cutOrder", "cut"]);
const cropperCanvas = ref(null);

const cut = () => {
    const img = new Image();
    img.crossOrigin = "Anonymous";  // 设置跨域属性

    img.src = props.imgURL;
    
    cropperCanvas.value.width = cutWidth.value;
    cropperCanvas.value.height = cutHeight.value;
    const ctx = cropperCanvas.value.getContext("2d");

    img.onload = () => {
        ctx.drawImage(img, cutOffsetX.value, cutOffsetY.value, cutWidth.value, cutHeight.value, 0, 0, cropperCanvas.value.width, cropperCanvas.value.height);
        emit("update:cutOrder", false);
        emit("cut", cropperCanvas.value.toDataURL('image/jpeg'));
    }

    img.onerror = (e) => {
        console.error("Image loading failed:", e);
    };
};

// 生命周期
onMounted(() => {
    updateOrigin();
    initDrag();
});

// 监听 props
watch(() => props.imgURL, (current) => {
    if (current) {
        updateOrigin();
    }
});

watch(() => props.cutOrder, (current) => {
    if (current) {
        cut();
    }
});
</script>


<style scoped>
.cropper {
    user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
}

.cropper-body {
    display: flex;
    position: relative;
}

.cropper-body-left {
    padding-top: 22px;
    width: 480px;
}

.img-wrp {
    height: 270px;
    width: 100%;
    border-radius: 4px;
    background-color: #000;
    overflow: hidden;
    text-align: center;
    position: relative;
}

.img-wrp>img {
    width: 100%;
    height: 100%;
    object-fit: scale-down;
}

.showPosition {
    position: absolute;
}

.crop-box {
    position: relative;
    border: 1px solid #fff;
    box-sizing: border-box;
}

.crop-box-move {
    z-index: 9;
    position: absolute;
    cursor: move;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
}

.crop-box-marking-left {
    z-index: 8;
    position: absolute;
    width: 12.5%;
    height: 100%;
    background: rgba(0, 0, 0, .2);
    border-right: 1px dashed rgba(255, 111, 111, .6);
    left: 0;
    top: 0;
}

.crop-box-marking-right {
    z-index: 8;
    position: absolute;
    width: 12.5%;
    height: 100%;
    background: rgba(0, 0, 0, .2);
    border-left: 1px dashed rgba(255, 111, 111, .6);
    right: 0;
    top: 0;
}

.crop-box-marking-top {
    z-index: 7;
    position: absolute;
    height: 12.5%;
    width: 100%;
    left: 0;
    top: 0;
}

.crop-box-marking-bottom {
    z-index: 7;
    position: absolute;
    height: 12.5%;
    width: 100%;
    left: 0;
    bottom: 0;
}

.cropper-point {
    position: absolute;
    background-color: #fff;
    height: 12.5px;
    opacity: 1;
    width: 12.5px;
    border-radius: 50%;
    z-index: 10;
}

.point-lt {
    cursor: nwse-resize;
    left: -6px;
    top: -6px;
}

.point-rt {
    cursor: nesw-resize;
    right: -6px;
    top: -6px;
}

.point-lb {
    cursor: nesw-resize;
    left: -6px;
    bottom: -6px;
}

.point-rb {
    cursor: nwse-resize;
    right: -6px;
    bottom: -6px;
}

.marking {
    position: absolute;
    background-color: rgba(0, 0, 0, .5);
}

.tips-wrp {
    font-weight: 400;
    font-size: 12px;
    line-height: 20px;
    color: var(--text2);
    margin-top: 12px;
}

.cropper-body-right {
    position: absolute;
    right: 0;
    transform: scale(.62);
    transform-origin: right 0;
    padding-right: 50px;
}

.preview-item {
    margin-top: 18px;
}

.preview-title {
    margin-bottom: 10px;
    position: relative;
    font-weight: 400;
    text-align: left;
    font-size: 20px;
    color: #19181c;
}

.preview-card {
    width: 224px;
    background: #fff;
    box-shadow: 0 2px 5px rgba(0, 0, 0, .1);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    align-items: center;
    position: relative;
}

.pic169 {
    position: relative;
    overflow: hidden;
    height: 126px;
    width: 100%;
    border-radius: 8px 8px 0 0;
    background-color: var(--graph_bg_regular);
}

.pic43 {
    position: relative;
    overflow: hidden;
    height: 168px;
    width: 100%;
    border-radius: 8px 8px 0 0;
    background-color: var(--graph_bg_regular);
}

.title {
    height: 65px;
    width: 100%;
}

.title span {
    margin: 8px 12px;
    font-weight: 400;
    font-size: 16px;
    line-height: 24px;
    color: #212121;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
    -webkit-line-clamp: 2;
    word-break: break-all;
}
</style>